"""
距离计算工具
"""
import time
from typing import Dict, Optional, Tuple, List
from django.core.cache import cache
from apps.attractions.models import Attraction
from apps.routes.models import RouteDistance
from .amap_client import AMapClient


class DistanceCache:
    """距离数据缓存"""
    
    def __init__(self, cache_time: int = 24 * 60 * 60):
        """
        初始化缓存
        :param cache_time: 缓存时间（秒），默认24小时
        """
        self.cache_time = cache_time
    
    def get_distance(self, origin_id: int, destination_id: int, 
                     transport_mode: str = 'driving') -> Optional[Dict]:
        """
        获取缓存的距离数据
        :param origin_id: 起始景点ID
        :param destination_id: 目标景点ID
        :param transport_mode: 交通方式
        :return: 距离数据或None
        """
        # 先从数据库查询
        try:
            route_distance = RouteDistance.objects.get(
                from_attraction_id=origin_id,
                to_attraction_id=destination_id,
                transport_mode=transport_mode
            )
            
            # 检查缓存是否过期
            if route_distance.cache_expire and route_distance.cache_expire.timestamp() > time.time():
                return {
                    'distance': float(route_distance.distance),
                    'travel_time': route_distance.travel_time,
                    'transport_cost': float(route_distance.transport_cost),
                    'transport_mode': route_distance.transport_mode
                }
        except RouteDistance.DoesNotExist:
            pass
        
        # 从Django缓存查询
        cache_key = f"distance_{origin_id}_{destination_id}_{transport_mode}"
        cached_data = cache.get(cache_key)
        if cached_data:
            return cached_data
        
        return None
    
    def set_distance(self, origin_id: int, destination_id: int, 
                    distance: float, travel_time: int = 0, 
                    transport_cost: float = 0,
                    transport_mode: str = 'driving'):
        """
        设置距离数据缓存
        :param origin_id: 起始景点ID
        :param destination_id: 目标景点ID
        :param distance: 距离（公里）
        :param travel_time: 行程时间（分钟）
        :param transport_cost: 交通费用（元）
        :param transport_mode: 交通方式
        """
        # 保存到数据库
        RouteDistance.objects.update_or_create(
            from_attraction_id=origin_id,
            to_attraction_id=destination_id,
            transport_mode=transport_mode,
            defaults={
                'distance': distance,
                'travel_time': travel_time,
                'transport_cost': transport_cost,
                'cache_expire': None  # 可以设置过期时间
            }
        )
        
        # 保存到Django缓存
        cache_key = f"distance_{origin_id}_{destination_id}_{transport_mode}"
        cache.set(cache_key, {
            'distance': distance,
            'travel_time': travel_time,
            'transport_cost': transport_cost,
            'transport_mode': transport_mode
        }, self.cache_time)


class DistanceCalculator:
    """距离计算器"""
    
    def __init__(self, amap_client: Optional[AMapClient] = None):
        """
        初始化计算器
        :param amap_client: 高德地图客户端
        """
        self.amap_client = amap_client or AMapClient()
        self.cache = DistanceCache()
    
    def calculate_distance(self, origin_id: int, destination_id: int,
                          transport_mode: str = 'driving',
                          use_cache: bool = True) -> Dict:
        """
        计算两个景点之间的距离（支持多种交通方式）
        :param origin_id: 起始景点ID
        :param destination_id: 目标景点ID
        :param transport_mode: 交通方式（driving, transit, walking, bicycling）
        :param use_cache: 是否使用缓存
        :return: 距离数据
        """
        if use_cache:
            cached_data = self.cache.get_distance(origin_id, destination_id, transport_mode)
            if cached_data:
                return cached_data
        
        # 从数据库获取景点信息
        try:
            origin = Attraction.objects.get(id=origin_id)
            destination = Attraction.objects.get(id=destination_id)
        except Attraction.DoesNotExist as e:
            raise ValueError(f"景点不存在: {str(e)}")
        
        # 调用高德地图API计算距离
        origin_coord = f"{origin.longitude},{origin.latitude}"
        dest_coord = f"{destination.longitude},{destination.latitude}"
        
        try:
            # 根据交通方式调用不同的API
            if transport_mode == 'driving':
                # 驾车：使用距离矩阵API
                result = self.amap_client.get_distance_matrix(
                    [origin_coord],
                    [dest_coord],
                    strategy=2  # 距离优先
                )
                if result.get('status') == '1' and result.get('results'):
                    distance_data = result['results'][0]
                    distance = float(distance_data.get('distance', 0)) / 1000  # 转换为公里
                    duration = int(distance_data.get('duration', 0)) / 60  # 转换为分钟
                else:
                    raise Exception(f"高德地图API返回数据异常: {result.get('info', '未知错误')}")
            else:
                # 其他交通方式：使用路径规划API
                try:
                    result = self.amap_client.get_route_by_mode(
                        origin_coord,
                        dest_coord,
                        transport_mode=transport_mode
                    )
                    
                    if result.get('status') == '1':
                        # 解析路径规划结果
                        route_data = self._parse_route_result(result, transport_mode)
                        distance = route_data['distance']
                        duration = route_data['duration']
                    else:
                        # API调用失败，使用直线距离估算
                        raise Exception(f"高德地图API返回数据异常: {result.get('info', '未知错误')}")
                except Exception as api_error:
                    # 如果API调用失败，抛出异常让外层处理（会回退到直线距离估算）
                    import logging
                    logger = logging.getLogger(__name__)
                    logger.warning(f"高德地图API调用失败 ({transport_mode}): {str(api_error)}")
                    # 重新抛出异常，让外层catch处理
                    raise
            
            # 计算交通费用
            transport_cost = self._calculate_transport_cost(distance, transport_mode)
            
            # 缓存结果
            self.cache.set_distance(
                origin_id, destination_id,
                distance, int(duration), transport_cost, transport_mode
            )
            
            return {
                'distance': distance,
                'travel_time': int(duration),
                'transport_cost': transport_cost,
                'transport_mode': transport_mode
            }
        except Exception as e:
            # API调用失败，使用直线距离估算
            import logging
            logger = logging.getLogger(__name__)
            logger.warning(f"距离计算API失败，使用估算值: {str(e)}")
            
            distance = self._calculate_straight_distance(
                float(origin.latitude), float(origin.longitude),
                float(destination.latitude), float(destination.longitude)
            )
            transport_cost = self._calculate_transport_cost(distance, transport_mode)
            # 根据交通方式估算时间
            travel_time = self._estimate_travel_time(distance, transport_mode)
            
            return {
                'distance': distance,
                'travel_time': travel_time,
                'transport_cost': transport_cost,
                'transport_mode': transport_mode
            }
    
    def _parse_route_result(self, result: Dict, transport_mode: str) -> Dict:
        """
        解析路径规划结果
        :param result: API返回结果
        :param transport_mode: 交通方式
        :return: 包含distance和duration的字典
        """
        import logging
        logger = logging.getLogger(__name__)
        
        routes = result.get('route', {})
        
        if transport_mode == 'transit':
            # 公交：取第一条路线
            # 高德地图公交API可能返回route.transits或直接返回transits
            transits = routes.get('transits', [])
            if not transits:
                # 尝试从result根目录获取
                transits = result.get('transits', [])
            
            if transits and len(transits) > 0:
                transit = transits[0]
                distance_str = transit.get('distance', '0')
                duration_str = transit.get('duration', '0')
                
                # 处理字符串或数字
                try:
                    distance = float(distance_str) / 1000  # 转换为公里
                    duration = int(float(duration_str)) / 60  # 转换为分钟
                except (ValueError, TypeError):
                    logger.warning(f"公交路线数据格式异常: distance={distance_str}, duration={duration_str}")
                    raise Exception("公交路线数据格式异常")
            else:
                # 如果没有找到公交路线
                logger.warning("未找到公交路线，API返回数据: " + str(result.get('route', {})))
                raise Exception("未找到公交路线")
        elif transport_mode in ['walking', 'bicycling']:
            # 步行和骑行
            paths = routes.get('paths', [])
            if not paths:
                # 尝试从result根目录获取
                paths = result.get('paths', [])
            
            if paths and len(paths) > 0:
                path = paths[0]
                distance_str = path.get('distance', '0')
                duration_str = path.get('duration', '0')
                
                # 处理字符串或数字
                try:
                    distance = float(distance_str) / 1000  # 转换为公里
                    duration = int(float(duration_str)) / 60  # 转换为分钟
                except (ValueError, TypeError):
                    logger.warning(f"{transport_mode}路线数据格式异常: distance={distance_str}, duration={duration_str}")
                    raise Exception(f"{transport_mode}路线数据格式异常")
            else:
                # 如果没有找到路线
                logger.warning(f"未找到{transport_mode}路线，API返回数据: " + str(result.get('route', {})))
                raise Exception(f"未找到{transport_mode}路线")
        else:
            raise ValueError(f"不支持的交通方式: {transport_mode}")
        
        return {
            'distance': distance,
            'duration': duration
        }
    
    def _estimate_travel_time(self, distance: float, transport_mode: str) -> int:
        """
        估算行程时间（分钟）
        :param distance: 距离（公里）
        :param transport_mode: 交通方式
        :return: 时间（分钟）
        """
        # 不同交通方式的平均速度（公里/小时）
        speed_map = {
            'driving': 40,  # 城市平均速度
            'transit': 25,  # 公交平均速度
            'bicycling': 15,  # 骑行平均速度
            'walking': 5,  # 步行平均速度
        }
        
        speed = speed_map.get(transport_mode, 20)
        return int((distance / speed) * 60)  # 转换为分钟
    
    def _calculate_straight_distance(self, lat1: float, lon1: float,
                                     lat2: float, lon2: float) -> float:
        """
        计算两点间的直线距离（公里）
        使用Haversine公式
        """
        from math import radians, cos, sin, asin, sqrt
        
        # 将十进制度数转化为弧度
        lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
        
        # Haversine公式
        dlon = lon2 - lon1
        dlat = lat2 - lat1
        a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
        c = 2 * asin(sqrt(a))
        r = 6371  # 地球平均半径，单位为公里
        
        return c * r
    
    def _calculate_transport_cost(self, distance: float, transport_mode: str) -> float:
        """
        根据交通方式计算费用
        :param distance: 距离（公里）
        :param transport_mode: 交通方式
        :return: 费用（元）
        """
        if transport_mode == 'driving':
            # 自驾：油费约0.8元/公里 + 停车费（估算）
            return distance * 0.8 + 5  # 每次停车约5元
        elif transport_mode == 'transit':
            # 公交：固定费用，根据距离估算换乘次数
            base_cost = 2.0  # 基础费用
            if distance > 10:
                # 超过10公里可能需要换乘，增加费用
                return base_cost + (distance - 10) / 5 * 2
            return base_cost
        elif transport_mode == 'bicycling':
            # 骑行：共享单车费用，按小时计费
            # 假设平均速度15km/h，超过1小时按2元/小时
            hours = distance / 15
            if hours <= 1:
                return 1.0  # 1小时内1元
            else:
                return 1.0 + (hours - 1) * 2  # 超过1小时每小时2元
        elif transport_mode == 'walking':
            # 步行：免费
            return 0.0
        else:
            # 默认：使用驾车费用
            return distance * 0.8 + 5
    
    def build_distance_matrix(self, attraction_ids: List[int],
                             transport_mode: str = 'driving',
                             use_cache: bool = True) -> Dict[Tuple[int, int], Dict]:
        """
        构建距离矩阵
        :param attraction_ids: 景点ID列表
        :param transport_mode: 交通方式
        :param use_cache: 是否使用缓存
        :return: 距离矩阵 {(起点ID, 终点ID): 距离数据}
        """
        matrix = {}
        
        for origin_id in attraction_ids:
            for dest_id in attraction_ids:
                if origin_id == dest_id:
                    continue
                
                key = (origin_id, dest_id)
                matrix[key] = self.calculate_distance(
                    origin_id, dest_id, transport_mode, use_cache
                )
        
        return matrix

